package com.fc.hkx.shiro.config;

import com.fc.hkx.service.ISysMenuService;
import com.fc.hkx.service.ISysRoleService;
import com.fc.hkx.service.ISysUserService;
import com.fc.hkx.shiro.MyModularRealmAuthenticator;
import com.fc.hkx.shiro.filter.MyFormAuthenticationFilter;
import com.fc.hkx.shiro.filter.MyLogoutFilter;
import com.fc.hkx.shiro.properties.ShiroSessionProps;
import com.fc.hkx.shiro.realm.SysUserRealm;
import org.apache.shiro.authc.Authenticator;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.SessionListener;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.session.mgt.eis.SessionIdGenerator;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.session.mgt.WebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.cache.ehcache.EhCacheCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

import javax.servlet.Filter;
import java.util.*;

/**
 * Created by Sunny on 2017-08-24.
 */
@Configuration
@EnableAutoConfiguration
@EnableConfigurationProperties(ShiroSessionProps.class)
public class ShiroConfiguration
{
    @Autowired(required = false)
    private Collection<SessionListener> listeners;

    @Bean(name = "lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor()
    {
        return new LifecycleBeanPostProcessor();
    }

    @Bean(name = "hashedCredentialsMatcher")
    public HashedCredentialsMatcher hashedCredentialsMatcher()
    {
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        credentialsMatcher.setHashAlgorithmName("SHA-256");
        return credentialsMatcher;
    }

    @Bean(name = "sysUserRealm")
    @DependsOn({"lifecycleBeanPostProcessor","sysUserService","sysMenuService","sysRoleService"})
    public Realm sysUserRealm(ISysUserService sysUserService, ISysMenuService sysMenuService, ISysRoleService sysRoleService) {
        SysUserRealm userRealm = new SysUserRealm();
        userRealm.setSysUserService(sysUserService);
        userRealm.setSysMenuService(sysMenuService);
        userRealm.setSysRoleService(sysRoleService);
        userRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return userRealm;
    }


    @Bean(name = "authenticator")
    @DependsOn({"sysUserRealm"})
    public ModularRealmAuthenticator authenticator(Realm sysUserRealm){
        MyModularRealmAuthenticator authenticator = new MyModularRealmAuthenticator();
        List<Realm> realms = new ArrayList<Realm>(2);
        realms.add(sysUserRealm);
        authenticator.setRealms(realms);
        return authenticator;
    }

    @Bean(name = "cacheManager")
    @DependsOn({"lifecycleBeanPostProcessor"})
    public CacheManager ehCacheManager(){
        EhCacheManager ehCacheManager = new EhCacheManager();
        ehCacheManager.setCacheManagerConfigFile("classpath:ehcache.xml");
        return ehCacheManager;
    }

    @Bean
    @ConditionalOnMissingBean
    @DependsOn({"shiroSessionProps"})
    public SessionDAO sessionDAO(CacheManager cacheManager, ShiroSessionProps shiroSessionProps) {
        EnterpriseCacheSessionDAO sessionDAO = new EnterpriseCacheSessionDAO();
        sessionDAO.setActiveSessionsCacheName(shiroSessionProps.getActiveSessionsCacheName());
        Class<? extends SessionIdGenerator> idGenerator = shiroSessionProps.getIdGenerator();
        if (idGenerator != null) {
            SessionIdGenerator sessionIdGenerator = BeanUtils.instantiate(idGenerator);
            sessionDAO.setSessionIdGenerator(sessionIdGenerator);
        }
        sessionDAO.setCacheManager(cacheManager);
        return sessionDAO;
    }


    @Bean
    @DependsOn(value = {"cacheManager", "sessionDAO", "shiroSessionProps"})
    public WebSessionManager sessionManager(CacheManager cacheManager, SessionDAO sessionDAO, ShiroSessionProps shiroSessionProps) {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setCacheManager(cacheManager);
        sessionManager.setGlobalSessionTimeout(shiroSessionProps.getGlobalSessionTimeout());

        sessionManager.setSessionDAO(sessionDAO);
        sessionManager.setSessionListeners(listeners);
        return sessionManager;
    }

    @Bean(name = "securityManager")
    @DependsOn("authenticator")
    public SecurityManager securityManager(Authenticator authenticator, CacheManager cacheManager, SessionManager sessionManager) {
        DefaultSecurityManager sm = new DefaultWebSecurityManager();
        sm.setAuthenticator(authenticator);
        sm.setCacheManager(cacheManager);
        sm.setSessionManager(sessionManager);
        //sm.setRememberMeManager(rememberMeManager);
        return sm;
    }


    @Bean(name = "shiroFilter")
    @DependsOn("securityManager")
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        Map<String, Filter> filters = new LinkedHashMap<String, Filter>();
        MyLogoutFilter logoutFilter = new MyLogoutFilter();
        logoutFilter.setRedirectUrl("/login");
        filters.put("logout", logoutFilter);
        MyFormAuthenticationFilter formAuthenticationFilter = new MyFormAuthenticationFilter();
        filters.put("authc", formAuthenticationFilter);
        shiroFilterFactoryBean.setFilters(filters);

        Map<String, String> filterChainDefinitionManager = new LinkedHashMap<String, String>();
        filterChainDefinitionManager.put("/logout", "logout");
        filterChainDefinitionManager.put("/sys/**", "authc");
        filterChainDefinitionManager.put("/api/register", "anon");
        filterChainDefinitionManager.put("/api/region/**", "anon");
        filterChainDefinitionManager.put("/user/**", "authc");
        filterChainDefinitionManager.put("/api/**", "anon");
        filterChainDefinitionManager.put("/login", "authc");
        filterChainDefinitionManager.put("/**", "anon");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionManager);

        shiroFilterFactoryBean.setLoginUrl("/login");
        shiroFilterFactoryBean.setSuccessUrl("/");
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");
        return shiroFilterFactoryBean;
    }

    @Bean(name = "shiroFilter")
    @DependsOn("securityManager")
    @ConditionalOnMissingBean
    public FilterRegistrationBean filterRegistrationBean(SecurityManager securityManager) throws Exception {
        FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
        //该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理
        filterRegistration.addInitParameter("targetFilterLifecycle", "true");
        filterRegistration.setFilter((Filter) shiroFilterFactoryBean(securityManager).getObject());
        filterRegistration.setEnabled(true);
        filterRegistration.addUrlPatterns("/*");
        return filterRegistration;
    }

    @Bean
    @ConditionalOnMissingBean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator daap = new DefaultAdvisorAutoProxyCreator();
        daap.setProxyTargetClass(true);
        return daap;
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor aasa = new AuthorizationAttributeSourceAdvisor();
        aasa.setSecurityManager(securityManager);
        return aasa;
    }
}
